package com.cloud.myhystrix.aspect;

import com.cloud.myhystrix.anno.MyHystrixCommand;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.*;

/**
 * @Author tangyuan2meicloud.com
 * @DATE 2021/7/2 8:19
 * @Desc
 * @Remarks
 */
@Aspect
@Component
public class MyHystrixAspect {

    ExecutorService executorService = Executors.newFixedThreadPool(10);

    @Pointcut(value = "@annotation(com.cloud.myhystrix.anno.MyHystrixCommand)")
    public void ponitCut() {
    }

    @Around("ponitCut() && @annotation(myHystrixCommand)")
    public Object doPointCut(ProceedingJoinPoint point, MyHystrixCommand myHystrixCommand) throws InterruptedException, ExecutionException, TimeoutException {
        int timeout = myHystrixCommand.timeout();
        Future future = executorService.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                try {
                    return point.proceed();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
                return null;
            }
        });
        try {
            return future.get(timeout, TimeUnit.MILLISECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            e.printStackTrace();
            if (StringUtils.isEmpty(myHystrixCommand.fallback())) {
                throw e;
            }
            return doFallbackMethod(point, myHystrixCommand);
        }
    }

    private Object doFallbackMethod(ProceedingJoinPoint point, MyHystrixCommand myHystrixCommand) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Class[] parameterTypes = signature.getParameterTypes();
        String fallback = myHystrixCommand.fallback();
        try {
            Method method = point.getTarget().getClass().getMethod(fallback, parameterTypes);
            return method.invoke(point.getTarget(), point.getArgs());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
        }
        return null;
    }
}
